`include "../codigo/Alu.v"
`include "../codigo/Shifter.v"

module Execute (clock, reset, id_ex_selalushift, id_ex_selimregb,
    id_ex_selsarega, id_ex_aluop, id_ex_unsig, id_ex_shiftop,
    id_ex_shiftamt, id_ex_rega, id_ex_msm, id_ex_msl,
    id_ex_readmem, id_ex_writemem, id_ex_mshw, id_ex_lshw,
    id_ex_regb, id_ex_imedext, id_ex_proximopc, id_ex_selwsource,
    id_ex_regdest, id_ex_writereg, id_ex_writeov, ex_fw_wbvalue,
    ex_fw_writereg, ex_if_stall, ex_mem_msm, ex_mem_msl,
    ex_mem_readmem, ex_mem_writemem,  ex_mem_mshw, ex_mem_lshw,
    ex_mem_regb, ex_mem_selwsource, ex_mem_regdest, ex_mem_writereg,
    ex_mem_aluout, ex_mem_wbvalue);

    input             clock;
	input             reset;

	// Decode
	input             id_ex_selalushift; // Seleciona ALU ou COMP
	input             id_ex_selimregb;   // Seleciona Imediato Extendido
                                         // ou Reg B
	input             id_ex_selsarega;   // Seleciona Shift Amount ou
                                         // Reg A
	input       [2:0] id_ex_aluop;       // Operacao da ALU
	input             id_ex_unsig;       // Informa se a instru��o 
                                         // sem sinal
	input       [1:0] id_ex_shiftop;     // Direcao do shift e 
                                         // Tipo do shif
	input       [4:0] id_ex_shiftamt;    // Shift Amount
	input      [31:0] id_ex_rega;
	input       [2:0] id_ex_msm;
	input       [2:0] id_ex_msl;
	input             id_ex_readmem;     // Le memoria
	input             id_ex_writemem;    // Escreve memoria
	input             id_ex_mshw;
	input             id_ex_lshw;
	input      [31:0] id_ex_regb;
	input      [31:0] id_ex_imedext;
	input      [31:0] id_ex_proximopc;
	input       [2:0] id_ex_selwsource;  // Seleciona fonte de dados
                                         // para WB realizar escrita
	input       [4:0] id_ex_regdest;     // Endere�o do registrador de
                                         // destino ( RD || RT || 31 )
	input             id_ex_writereg;    // Escreve em registrador
	input             id_ex_writeov;     // Escreve c/ overflow 

	// Forwarding
	output     [31:0] ex_fw_wbvalue;     // Valor que ser� gravado em WB
	output            ex_fw_writereg;    // Flag para FW saber se este
                                // valor ser� usado (gravado = usado)

	// Fetch
	output reg        ex_if_stall;

	// Memory
	output reg  [2:0] ex_mem_msm;
	output reg  [2:0] ex_mem_msl;
	output reg        ex_mem_readmem;    // Le memoria
	output reg        ex_mem_writemem;   // Escreve memoria
	output reg        ex_mem_mshw;
	output reg        ex_mem_lshw;
	output reg [31:0] ex_mem_regb;
	output reg  [2:0] ex_mem_selwsource; // Seleciona fonte de dados
                                         // para WB realizar escrita
	output reg  [4:0] ex_mem_regdest;    // Endere�o do registrador
                                         // de destino ( RD || RT || 31 )
	output reg        ex_mem_writereg;   // Escreve em registrador
	output reg [31:0] ex_mem_aluout;     // Resultado de ( SHIFT || ALU )
	output reg [31:0] ex_mem_wbvalue;

    // alu(a, b, aluop, aluout, unsig, compout, overflow)
    wire [31:0] aluout;
    wire [0:0] compout;
    wire [0:0] aluov;

    // shifter(value_in, shiftop, shiftamt, result)
    wire [31:0] result;

	Shifter     SHIFTER(id_ex_regb, id_ex_shiftop, mux_sarega, result);
	Alu         ALU(id_ex_rega, mux_imregb, id_ex_aluop, aluout,
        id_ex_unsig, compout, aluov);


    // --------------------------------------------------------------
    // Wire assignments

    // ALU / shifter output
    wire [31:0] mux_alusft;
    reg [31:0] mux_alusft_reg;
    assign mux_alusft = mux_alusft_reg;
    
    // Register B / sign extended value
    wire [31:0] mux_imregb;
    reg [31:0] mux_imregb_reg;
    assign mux_imregb = mux_imregb_reg;

    // Register A / shift amount
    wire [4:0] mux_sarega;
    reg [4:0] mux_sarega_reg;
    assign mux_sarega = mux_sarega_reg;

    // Writeback
    wire [31:0] mux_wbvalue;
    reg [31:0] mux_wbvalue_reg;
    assign mux_wbvalue = mux_wbvalue_reg;

    // Writeback value
    assign ex_fw_wbvalue = mux_wbvalue;
    assign ex_fw_writereg = id_ex_writereg;


    // --------------------------------------------------------------
    // Register assignments
    always @(negedge(clock)) begin

        // If under memory access read/write
        if (id_ex_readmem | id_ex_writemem) begin
            ex_if_stall = 1'b1;
        end else begin
            ex_if_stall = 1'bx;
        end

        // Register A / shift amount
        case (id_ex_selsarega)
            
            1'b0: begin
                mux_sarega_reg = id_ex_rega[4:0];
            end
            
            1'b1: begin
                mux_sarega_reg = id_ex_shiftamt;
            end

            default: begin
                mux_sarega_reg = 32'bx;
            end

        endcase

        // Register B / sign extended value
        #1 case (id_ex_selimregb)

            1'b0: begin
                mux_imregb_reg = id_ex_regb;
            end

            1'b1: begin
                mux_imregb_reg = id_ex_imedext;
            end

            default: begin
                mux_imregb_reg = 32'bx;
            end

        endcase

        // ALU / shifter output
        #1 case (id_ex_selalushift)
        
            1'b0: begin
                mux_alusft_reg = aluout;
            end

            1'b1: begin
                mux_alusft_reg = result;
            end

            default: begin
                mux_alusft_reg = 32'bx;
            end

        endcase

        // Writeback value
        case (id_ex_selwsource)

            3'b000: begin
                mux_wbvalue_reg = mux_alusft;
            end

            3'b010: begin
                mux_wbvalue_reg = id_ex_imedext;
            end

            3'b011: begin
                mux_wbvalue_reg = id_ex_proximopc;
            end

            3'b100: begin
                mux_wbvalue_reg = (32'b0 | compout);
            end

            default: begin
                mux_wbvalue_reg = 32'bz;
            end

        endcase

        // Values transferred to the memory unit
        ex_mem_msm = id_ex_msm;
        ex_mem_msl = id_ex_msl;
        ex_mem_readmem = id_ex_readmem;
        ex_mem_writemem = id_ex_writemem;
        ex_mem_mshw = id_ex_mshw;
        ex_mem_lshw = id_ex_lshw;
        ex_mem_regb = id_ex_regb;
        ex_mem_selwsource = id_ex_selwsource;
        ex_mem_regdest = id_ex_regdest;
        ex_mem_writereg = (!aluov | id_ex_writeov) & id_ex_writereg;
        ex_mem_aluout = mux_alusft;
        ex_mem_wbvalue = mux_wbvalue;

    end


    // Resets values
    always @(negedge(reset)) begin

        ex_mem_msm = 3'b0;
        ex_mem_msl = 3'b0;
        ex_mem_readmem = 1'b0;
        ex_mem_writemem = 1'b0;
        ex_mem_mshw = 1'b0;
        ex_mem_lshw = 1'b0;
        ex_mem_regb = 32'b0;
        ex_mem_selwsource = 3'b0;
        ex_mem_regdest = 5'b0;  
        ex_mem_writereg = 1'b0;
        ex_mem_aluout = 32'b0;
        ex_mem_wbvalue = 32'b0;

    end

endmodule
